using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
using Athena;
using Athena.Tools;
using Athena.Objects;
using Athena.Core;
using Athena.Plugins;

using WeifenLuo.WinFormsUI.Docking;

namespace PromoterEquationGUI
{
    public partial class PromoterEquationGUI : Athena.Tools.IGUITool
    {

        public override void SetupToolStrip(ToolStrip oStrip)
        {
            ToolStripButton oButton = new ToolStripButton();
            oButton.Text = Text;
            oButton.ToolTipText = "A graphical tool for finding transcription rates";
            oButton.Click += new EventHandler(ShowHide_Click);
            oStrip.Items.Add(oButton);
        }

        public override void SetupDockPanel(WeifenLuo.WinFormsUI.Docking.DockPanel oPanel)
        {
            this.DockPanel = oPanel;
        }

        public override void ShowHide_Click(object sender, EventArgs e)
        {
            if (IsHidden)
            {
                //DockState = WeifenLuo.WinFormsUI.Docking.DockState.Float;
                Show();
                if (CurrentModel.Selected.Count == 1)
                    if (CurrentModel.Selected[0].Item is Edge)
                    {
                        TranscriptionRate.InitializeWithEdge((Edge)CurrentModel.Selected[0].Item);
                        AutoGenerateStates(TranscriptionRate.ProteinNames.Length);
                        namesTextBox.Text = string.Join(" ", TranscriptionRate.ProteinNames);
                    }
                    else
                        if (CurrentModel.Selected[0].Item is Part)
                        {
                            Part p = (Part)CurrentModel.Selected[0].Item;
                            if (p.PoPS != null)
                            {
                                TranscriptionRate.InitializeWithEdge(p.PoPS);
                                AutoGenerateStates(TranscriptionRate.ProteinNames.Length);
                                namesTextBox.Text = string.Join(" ", TranscriptionRate.ProteinNames);
                            }
                        }
            }
            else
            {
                Hide();
            }
        }


        void MainForm_FormClosing(object sender, System.Windows.Forms.FormClosingEventArgs e)
        {
            Hide();
            e.Cancel = true;
        }

        public PromoterEquationGUI()
        {
            InitializeComponent();

            SetStyle(ControlStyles.AllPaintingInWmPaint |
                ControlStyles.OptimizedDoubleBuffer |
                ControlStyles.ResizeRedraw |
                ControlStyles.UserPaint, true);

            States = new List<GeneState>();
            AutoGenerateStates(2);
            IndependentBinding = false;
            Text = "Transcription";
            _bRequiresNewToolWindow = true;
            ToolManager.Instance.RegisterTool(Text, this);

            TranscriptionRate.DefaultNames(2);
        }

        private List<GeneState> States;
        private bool IndependentBinding = false;
        private float _offset = 0;

        private class GeneState
        {
            public double KdRpol;
            public double[] Kd;
            public bool[] BoundSites;
            public int nBindingSites;
            public float Width, X, Y;
            public bool Selected;

            public GeneState(int BindingSites, float x_loc, float y_loc, float _Width, Panel panel, string[] labels)
            {
                if (TranscriptionRate.ProteinNames == null) TranscriptionRate.DefaultNames(BindingSites);
                nBindingSites = BindingSites;
                BoundSites = new bool[BindingSites];
                Kd = new double[BindingSites];
                KdRpol = 1;
                Width = _Width;
                X = x_loc;
                Y = y_loc;
                Selected = false;

                for (int i = 0; i < BindingSites; ++i) Kd[i] = 1;

            }

            /// <summary>
            /// Draws the gene with the binding sites and bound/unbound proteins and textbox inputs
            /// </summary>
            /// <param name="g"></param>
            public void Draw(Graphics g)
            {
                float circleSz = Width / 3;

                Pen genePen = new Pen(Color.Green, 3),
                    bindingPen = new Pen(Color.DarkOrange, 3),
                    circlePen = new Pen(Color.Firebrick, 3),
                    linePen = new Pen(Color.IndianRed, 2);

                if (Selected)
                {
                    genePen.Color = Color.Red;
                    circlePen.Color = Color.Red;
                    bindingPen.Color = Color.Red;
                    linePen.Color = Color.Red;
                }

                g.DrawLine(genePen, X, Y, X + Width * nBindingSites, Y);

                g.DrawString("Kd(Polymerase) = " + KdRpol, SystemFonts.DialogFont, linePen.Brush, new PointF((float)(X + Width * nBindingSites + 50), (float)(Y - 40)));
                
                for (int i = 0; i < BoundSites.Length; ++i)
                {
                    //the binding site
                    g.DrawLine(bindingPen, X + Width / 3 + Width * i, Y - 0.5F, X + Width / 3 + Width * i + circleSz, Y - 0.5F);

                    g.DrawString("Kd(" + TranscriptionRate.ProteinNames[i] + ") = " + Kd[i], SystemFonts.DialogFont, linePen.Brush, new PointF((float)(X + Width / 3 + Width * i + Width / 3.0), (float)(Y - 3 * Width / 4) + i*2));

                    //draw each binding protein
                    if (BoundSites[i])  //bound on gene
                    {
                        g.FillEllipse(circlePen.Brush, X + Width / 3 + Width * i, Y - circleSz - 1, circleSz, circleSz);
                    }
                    else   //unbound, on top
                    {                        
                        g.FillEllipse(circlePen.Brush, X + Width / 3 + Width * i, Y - circleSz * 3, circleSz, circleSz);

                        linePen.DashStyle = DashStyle.Dash;
                        g.DrawLine(linePen, X + Width / 3 + Width * i + circleSz / 2, Y - circleSz * 2, X + Width / 3 + Width * i + circleSz / 2, Y - 1);
                        g.DrawLine(linePen, X + Width / 3 + Width * i + circleSz / 2 - 5, Y - circleSz / 2, X + Width / 3 + Width * i + circleSz / 2, Y - 1);
                        g.DrawLine(linePen, X + Width / 3 + Width * i + circleSz / 2 + 5, Y - circleSz / 2, X + Width / 3 + Width * i + circleSz / 2, Y - 1);
                    }
                }
            }
            /// <summary>
            /// Checks what area of the genestate was clicked on. Returns the index of the binding site clicked on
            /// If the gene was clicked on, returns nBindingSite. Returns -1 if nothing was clicked on
            /// </summary>
            /// <param name="x_loc"></param>
            /// <param name="y_loc"></param>
            /// <returns></returns>
            public int ClickedOn(float x_loc, float y_loc)
            {
                float circleSz = Width / 3;

                for (int i = 0; i < BoundSites.Length; ++i)
                {
                    if (BoundSites[i])
                    {
                        RectangleF rect = new RectangleF(X + Width / 3 + Width * i, Y - circleSz - 1, circleSz, circleSz);
                        if (rect.Contains(new PointF(x_loc, y_loc)))
                        {
                            return i;
                        }
                    }
                    else
                    {
                        RectangleF rect = new RectangleF(X + Width / 3 + Width * i, Y - circleSz * 3, circleSz, circleSz);
                        if (rect.Contains(new PointF(x_loc, y_loc)))
                        {
                            return i;
                        }
                    }
                }
                if (x_loc > X &&
                    x_loc < (X + Width * nBindingSites + 200) &&
                    y_loc > (Y - 3 * circleSz - 1) &&
                    y_loc < (Y + 5))
                {
                    return nBindingSites;
                }

                return -1;
            }

        }

        private void Draw(Graphics g)
        {
            
            float x = 10, y = _offset + panel1.Height;

            foreach (GeneState state in States)
            {
                state.Y = y;
                state.X = x;

                x += state.Width * state.nBindingSites * 3;

                if (x + state.Width * state.nBindingSites > drawPanel.Width)
                {
                    y += state.Width + 30;
                    x = 10;
                }

                //if (state.Y > 0 && state.Y < drawPanel.Height + 50)
                //{
                state.Draw(g);
                //}
            }
            _endLabel.Location = new Point(0, 5000);
        }

        private void printDocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
        {
            e.Graphics.SmoothingMode = SmoothingMode.None;
            e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault;

            Draw(e.Graphics);
        }

        private void drawPanel_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;

            Draw(e.Graphics);
        }

        int _CurrentState = 0, nBindingSites;
        NumericUpDown[] _EditBoxes;

        private void ShowEditBoxes()
        {
            if (States.Count < 1 || _CurrentState < 0 || States.Count <= _CurrentState || States[_CurrentState].nBindingSites + 1 != _EditBoxes.Length)
                return;
            int i = 0;
            for (; i < States[_CurrentState].nBindingSites; ++i)
            {
                _EditBoxes[i].Value = (decimal)States[_CurrentState].Kd[i];
                _EditBoxes[i].Location = new Point((int)(States[_CurrentState].X + States[_CurrentState].Width / 3 + States[_CurrentState].Width * i + States[_CurrentState].Width / 3.0), (int)(States[_CurrentState].Y - States[_CurrentState].Width / 2));
                _EditBoxes[i].Width = 52;
                _EditBoxes[i].Visible = true;
            }
            _EditBoxes[i].Location = new Point((int)(States[_CurrentState].X + States[_CurrentState].Width / 3 + States[_CurrentState].Width * i + States[_CurrentState].Width / 3.0), (int)(States[_CurrentState].Y - States[_CurrentState].Width / 2));
            _EditBoxes[i].Width = 52;
            _EditBoxes[i].Value = (decimal)States[_CurrentState].KdRpol;
            _EditBoxes[i].Visible = true;
        }

        private void HideEditBoxes()
        {
            if (States.Count < 1 || _CurrentState < 0 || States.Count <= _CurrentState || States[_CurrentState].nBindingSites+1 != _EditBoxes.Length)
                return;

            for (int i = 0; i < States[_CurrentState].nBindingSites; ++i)
            {
                if (_EditBoxes[i].Visible)
                    States[_CurrentState].Kd[i] = (double)_EditBoxes[i].Value;
                States[_CurrentState].Selected = false;
                _EditBoxes[i].Visible = false;
            }
            if (_EditBoxes[States[_CurrentState].nBindingSites].Visible)
                States[_CurrentState].KdRpol = (double)_EditBoxes[States[_CurrentState].nBindingSites].Value;
            _EditBoxes[States[_CurrentState].nBindingSites].Visible = false;
        }

        private void drawPanel_MouseDown(object sender, MouseEventArgs e)
        {
            HideEditBoxes();
            for (int i = 0; i < States.Count; ++i)
            {
                if (States[i].Y < 100 || States[i].Y > drawPanel.Height) continue;

                int clicked = States[i].ClickedOn(e.X, e.Y);

                if (IndependentBinding)
                {
                    foreach (GeneState state in States)
                    {
                        for (int j = 0; j < state.Kd.Length; ++j)
                        {
                            state.Kd[j] = States[i].Kd[j];
                        }
                    }
                }

                if (clicked >= 0)
                {
                    //setup the up-down box to edit the selected Kd
                    
                    _CurrentState = i;
                    ShowEditBoxes();

                    States[i].Selected = true;

                    
                    if (clicked < States[i].nBindingSites)
                    {
                        if (e.Button == MouseButtons.Left)
                        {
                            States[i].BoundSites[clicked] = !States[i].BoundSites[clicked];
                        }
                    }
                }
                else
                {
                    if (Control.ModifierKeys != Keys.Control)
                        States[i].Selected = false;
                }
            }
            
            drawPanel.Refresh();
        }
        private void drawPanel_MouseMove(object sender, MouseEventArgs e)
        {
        }
        private void drawPanel_MouseUp(object sender, MouseEventArgs e)
        {
        }
        private void drawPanel_MouseDoubleClick(object sender, MouseEventArgs e)
        {
        }
        private void drawPanel_MouseLeave(object sender, EventArgs e)
        {
        }
        private void drawPanel_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            _offset += (int)e.Delta / 10;
            if (_offset < -(States.Count - 1) * (States[0].Width + 30)) _offset = -(States.Count - 1) * (States[0].Width + 30);
            if (_offset > (States.Count - 1) * (States[0].Width + 30)) _offset = (States.Count - 1) * (States[0].Width + 30);
            HideEditBoxes();
            drawPanel.Refresh();
        }

        private void drawPanel_KeyDown(object sender, KeyEventArgs e)
        {
            HideEditBoxes();
            switch (e.KeyCode)
            {
                case Keys.Delete:
                    for (int i = 0; i < States.Count; ++i)
                    {
                        if (States[i].Selected)
                        {   
                            States.RemoveAt(i);
                        }
                    }
                    break;
                case Keys.PageUp:
                    _offset += 20;
                    if (_offset < -(States.Count - 1) * (States[0].Width + 30)) _offset = -(States.Count - 1) * (States[0].Width + 30);
                    if (_offset > (States.Count - 1) * (States[0].Width + 30)) _offset = (States.Count - 1) * (States[0].Width + 30);
                    break;
                case Keys.PageDown:
                    _offset -= 20;
                    if (_offset < -(States.Count - 1) * (States[0].Width + 30)) _offset = -(States.Count - 1) * (States[0].Width + 30);
                    if (_offset > (States.Count - 1) * (States[0].Width + 30)) _offset = (States.Count - 1) * (States[0].Width + 30);
                    break;
                case Keys.Up:
                    _offset += 20;
                    if (_offset < -(States.Count - 1) * (States[0].Width + 30)) _offset = -(States.Count - 1) * (States[0].Width + 30);
                    if (_offset > (States.Count - 1) * (States[0].Width + 30)) _offset = (States.Count - 1) * (States[0].Width + 30);
                    break;
                case Keys.Down:
                    _offset -= 20;
                    if (_offset < -(States.Count - 1) * (States[0].Width + 30)) _offset = -(States.Count - 1) * (States[0].Width + 30);
                    if (_offset > (States.Count - 1) * (States[0].Width + 30)) _offset = (States.Count - 1) * (States[0].Width + 30);
                    break;
                case Keys.Enter:
                    HideEditBoxes();
                    break;
                case Keys.P:
                    if (ModifierKeys == Keys.Control)
                    {
                        if (printDialog.ShowDialog() == DialogResult.OK)
                        {
                            printDocument.PrinterSettings = printDialog.PrinterSettings;
                            printDocument.Print();
                        }
                    }
                    break;
                default:
                    break;
            }
            drawPanel.Refresh();
        }

        void drawPanel_Scroll(object sender, System.Windows.Forms.ScrollEventArgs e)
        {
            HideEditBoxes();
            _offset += e.OldValue - e.NewValue;
            drawPanel.Refresh();
        }


        private void copyToPromButton_Click(object sender, EventArgs e)
        {
            if (CurrentModel.Selected.Count > 0)
            {
                foreach (IDrawable id in CurrentModel.Selected)
                {
                    if (id.Item is Part)
                    {
                        Part part = (Part)(id.Item);
                        if (part != null)
                        {
                            part.PoPS.Rate = rateTextBox.Text;
                            for (int i = 0; i < TranscriptionRate.ParameterValues.Count; ++i) //write out parameters in the text box
                            {
                                part.PoPS.SetParameter("k" + (i + 1), TranscriptionRate.ParameterValues[i]);
                            }
                        }
                        part.PoPS.AssignParameterNames();
                    }
                    else
                        if (id.Item is Edge)
                        {
                            Edge edge = (Edge)(id.Item);
                            if (edge != null)
                            {
                                edge.Rate = rateTextBox.Text;
                                for (int i = 0; i < TranscriptionRate.ParameterValues.Count; ++i) //write out parameters in the text box
                                {
                                    edge.SetParameter("k" + (i + 1), TranscriptionRate.ParameterValues[i]);
                                }
                            }
                            edge.AssignParameterNames();
                        }
                }
                CurrentModel.OnModelChanged(EventArgs.Empty);
            }
        }

        private void button_Click(object sender, EventArgs e)
        {
            HideEditBoxes();
            try
            {
                FillPromoterTables();
                rateTextBox.Text = TranscriptionRate.sGetProb(States[0].nBindingSites);
                parameterTextBox.Text = "";
                for (int i = 0; i < TranscriptionRate.ParameterValues.Count; ++i) //write out parameters in the text box
                {
                    parameterTextBox.Text += string.Format("k{0} = {1}; ", i + 1, TranscriptionRate.ParameterValues[i]);
                }
                //rateTextBox.Width = rateTextBox.Text.Length * 10;                
            }
            catch (Exception ex)
            {
                MessageBox.Show(String.Format("Please make sure, that your value is in a reasonable range. {0} {0}Detailed Errormessage: {1}", Environment.NewLine, ex.Message), "Exception occured", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            drawPanel.Focus();
        }

        private void FillPromoterTables()
        {
            int nBindingSites = States[0].nBindingSites;

            TranscriptionRate.RPolBindingProb = new double[(int)Math.Pow(2, nBindingSites)];
            TranscriptionRate.ProteinBindingProb = new double[nBindingSites, (int)Math.Pow(2, nBindingSites)];

            double Kd = 0;

            for (int i = 0; i < States.Count; ++i)
            {
                int[] temp = new int[nBindingSites];
                for (int j = 0; j < nBindingSites; ++j)   //get the vector for the row
                {
                    if (States[i].BoundSites[j])
                        temp[j] = 1;
                    else
                        temp[j] = 0;
                }

                int x = TranscriptionRate.ArrayToInt(temp);    //convert binary array to int 

                for (int j = 0; j < nBindingSites; ++j)
                {
                    Kd = 0;

                    Kd = States[i].Kd[j];
                    if (Kd > 0)
                        TranscriptionRate.ProteinBindingProb[j, x] = 1 / Kd; //set the value
                }

                Kd = 0;

                Kd = States[i].KdRpol; 
                if (Kd > 0)
                    TranscriptionRate.RPolBindingProb[x] = 1 / Kd;
            }
        }

        private void drawPanel_Click(object sender, EventArgs e)
        {
            drawPanel.Focus();
        }

        private void reset_Click(object sender, EventArgs e)
        {
            HideEditBoxes();
            try
            {
                int numSites = (int)(numSitesBox.Value);

                States.Clear();
                AutoGenerateStates(numSites);
                IndependentBinding = indepCheck.Checked;
                
                string[] names = namesTextBox.Text.Split(' ', ',');
                for (int i = 0; i < names.Length && i < TranscriptionRate.ProteinNames.Length; ++i)
                {
                    TranscriptionRate.ProteinNames[i] = names[i];
                }

                drawPanel.Refresh();
                drawPanel.Focus();
            }
            catch (Exception)
            {
                MessageBox.Show("Value must be an integer.", "Invalid Argument", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void indepCheck_CheckedChanged(object sender, EventArgs e)
        {
            IndependentBinding = indepCheck.Checked;
            drawPanel.Focus();
        }

        /// <summary>
        /// Automatically generates all the possible states
        /// </summary>
        /// <param name="nBindingSites"></param>
        private void AutoGenerateStates(int n)
        {
            HideEditBoxes();
            States.Clear();

            nBindingSites = n;
            _EditBoxes = new NumericUpDown[n+1];

            drawPanel.Controls.Clear();

            for (int i = 0; i < _EditBoxes.Length; ++i)
            {
                _EditBoxes[i] = new NumericUpDown();
                _EditBoxes[i].Visible = false;
                _EditBoxes[i].Increment = (decimal)0.001;
                _EditBoxes[i].DecimalPlaces = 3;
                drawPanel.Controls.Add(_EditBoxes[i]);
            }

            TranscriptionRate.FillBooleanTable(n);
            for (int i = 0; i < TranscriptionRate.booleanTable.GetLength(0); ++i)
            {
                GeneState state = new GeneState(n, 0, 0, 80, drawPanel, null);
                for (int j = 0; j < TranscriptionRate.booleanTable.GetLength(1); ++j)
                {
                    state.BoundSites[j] = TranscriptionRate.booleanTable[i, j] == 1;
                }
                States.Add(state);
            }

            drawPanel.Refresh();
            drawPanel.Focus();
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            string[] names = namesTextBox.Text.Split(' ', ',');
            for (int i = 0; i < names.Length && i < TranscriptionRate.ProteinNames.Length; ++i)
            {
                TranscriptionRate.ProteinNames[i] = names[i];
            }
        }

        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {
            HideEditBoxes();
            for (int i = 0; i < States.Count; ++i)
            {
                States[i].Width = (float)numericUpDown1.Value;
            }
            drawPanel.Refresh();
            drawPanel.Focus();
        }

        private void clearButton_Click(object sender, EventArgs e)
        {
            HideEditBoxes();
            States.Clear();
            drawPanel.Refresh();
            drawPanel.Focus();
        }

        private void addStateButton_Click(object sender, EventArgs e)
        {
            HideEditBoxes();
            float x = 10, y = 10;
            if (States.Count > 0)
            {
                x = States[States.Count - 1].X;
                y = States[States.Count - 1].Y + 30;
            }

            GeneState newState = new GeneState(
                                (int)numSitesBox.Value,
                                x, y, (float)numericUpDown1.Value,
                                drawPanel, null);
            for (int j = 0; j < newState.nBindingSites; ++j)
            {
                newState.BoundSites[j] = false;
            }
            States.Add(newState);
            drawPanel.Refresh();
            drawPanel.Focus();
        }

        private void numSitesBox_ValueChanged(object sender, EventArgs e)
        {
            reset.Focus(); 
        }

        private void rateTextBox_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            rateTextBox.SelectAll();
        }

        private void rateTextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            rateTextBox.SelectAll();
        }
        /// <summary>
        /// Prevent some nasty keys ...
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="keyData"></param>
        /// <returns></returns>
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            if (!Focused) return false;

            switch (keyData)
            {
                case Keys.Up:
                case Keys.Down:
                case Keys.Back:
                case Keys.Tab:
                case Keys.Enter:
                case Keys.Home:
                case Keys.PageDown:
                case Keys.PageUp:
                case Keys.Control & Keys.P:
                    return true;

                default:
                    return base.ProcessCmdKey(ref msg, keyData);
            }
        }


    }
}